package com.tianji.promotion.service.impl;

import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Maps;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.constants.PromotionConstants;
import com.tianji.promotion.discount.Discount;
import com.tianji.promotion.discount.DiscountStrategy;
import com.tianji.promotion.domain.dto.CouponDiscountDTO;
import com.tianji.promotion.domain.dto.OrderCouponDTO;
import com.tianji.promotion.domain.dto.OrderCourseDTO;
import com.tianji.promotion.domain.dto.UserCouponDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.UserCouponQuery;
import com.tianji.promotion.domain.vo.UserCouponPageVO;
import com.tianji.promotion.enums.ExchangeCodeStatus;
import com.tianji.promotion.enums.MyLockType;
import com.tianji.promotion.enums.UserCouponStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.utils.CodeUtil;
import com.tianji.promotion.utils.MyLock;
import com.tianji.promotion.utils.MyLockStrategy;
import com.tianji.promotion.utils.PermuteUtil;
import jodd.util.CollectionUtil;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisPipelineException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author hrz
 * @since 2025-09-02
 */
@Service
@RequiredArgsConstructor
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {

    private final CouponMapper couponMapper;
    private final StringRedisTemplate redisTemplate;
    private final IExchangeCodeService exchangeCodeService;
    private final RabbitMqHelper rabbitMqHelper;
    private final ICouponScopeService couponScopeService;
    private final Executor calculateExec;

    @MyLock(key = "lock:coupon:uid:#{id}",
            type = MyLockType.RE_ENTRANT_LOCK,
            waitTime = 5,
            strategy = MyLockStrategy.FAIL_AFTER_RETRY_TIMEOUT)
    public void receiveCoupon(Long id) {
        //1.查询优惠券
        Map<Object,Object> entries = redisTemplate.opsForHash()
                .entries(PromotionConstants.COUPON_CACHE_KEY_PREFIX + id);
        //2.判断优惠券是否存在
        if(entries.isEmpty()) {
            throw new IllegalArgumentException("优惠券不存在");
        }
        Coupon coupon = BeanUtils.mapToBean(entries, Coupon.class, false, CopyOptions.create());
        //3.判断优惠券的发放时间是否过期
        LocalDateTime now = LocalDateTime.now();
        if (now.isBefore(coupon.getIssueBeginTime()) || now.isAfter(coupon.getIssueEndTime())) {
            throw new IllegalArgumentException("优惠券已过期或者还未开始发放");
        }
        //4.判断优惠券的库存是否充足
        if (coupon.getTotalNum() <= 0) {
            throw new IllegalArgumentException("优惠券已发放完");
        }
        //5.判断当前用户是否领取超过上限
        String key = PromotionConstants.USER_COUPON_CACHE_KEY_PREFIX + id;
        //从redis中获取,如果是第一次领取,就新建一个键
        Long count = redisTemplate.opsForHash().increment(key,
                UserContext.getUser().toString(), 1);
        if (count >= coupon.getUserLimit()) {
            throw new IllegalArgumentException("优惠券已达单人领取上限");
        }
        // 6.扣减优惠券库存
        redisTemplate.opsForHash().increment(
                PromotionConstants.COUPON_CACHE_KEY_PREFIX + id, "totalNum", -1);
        //int i = couponMapper.updateIssueNum(id);
        //updateCouponAndCode(id,coupon,null,UserContext.getUser());

        //7.发送mq消息,异步领券
        UserCouponDTO uc = new UserCouponDTO();
        uc.setUserId(UserContext.getUser());
        uc.setCouponId(id);
        rabbitMqHelper.send(
                MqConstants.Exchange.Promotion_EXCHANGE,
                MqConstants.Key.COUPON_RECEIVE,
                uc
        );
    }

    @Override
    @MyLock(key = "lock:coupon:uid:#{T(com.tianji.common.utils.UserContext).getUser()}",
            type = MyLockType.RE_ENTRANT_LOCK,
            waitTime = 5,
            strategy = MyLockStrategy.FAIL_AFTER_RETRY_TIMEOUT)
    public void exchangeCoupon(String code) {
        if (StringUtils.isBlank(code)) {
            throw new IllegalArgumentException("兑换码不能为空");
        }
        //1.校验兑换码
        long serialNum = CodeUtil.parseCode(code);
        //2.取bitmap上查看是否兑换过了 - 设置为1并查看返回值
        Boolean excahnge = redisTemplate.opsForValue().setBit(
                PromotionConstants.COUPON_CODE_MAP_KEY, serialNum, true);
        if (excahnge != null && excahnge) {
            //说明兑换过了
            throw new IllegalArgumentException("兑换码已被使用");
        }
        try {
            //3.去查询兑换码
            ExchangeCode byId = exchangeCodeService.getById(serialNum);
            if (byId == null) {
                throw new IllegalArgumentException("兑换码不存在");
            }
            if (LocalDateTime.now().isAfter(byId.getExpiredTime())) {
                throw new IllegalArgumentException("兑换码已过期");
            }
            //4.判断优惠券的情况
            Long exchangeTargetId = byId.getExchangeTargetId(); //优惠券的id
            //去缓存中查找优惠券的信息
            Map<Object, Object> entries = redisTemplate.opsForHash()
                    .entries(PromotionConstants.COUPON_CACHE_KEY_PREFIX + exchangeTargetId);
            Coupon coupon = BeanUtils.mapToBean(entries, Coupon.class, false, CopyOptions.create());
            //5.判断优惠券是否存在
            if (coupon == null) {
                throw new IllegalArgumentException("优惠券不存在");
            }
            //6.判断优惠券的库存是否充足
            if (coupon.getTotalNum() <= 0) {
                throw new IllegalArgumentException("优惠券已发放完");
            }
            //7.判断当前用户是否领取超过上限
            String key = PromotionConstants.USER_COUPON_CACHE_KEY_PREFIX + exchangeTargetId;
            //从redis中获取,如果是第一次领取,就新建一个键
            Long count = redisTemplate.opsForHash().increment(key,
                    UserContext.getUser().toString(), 1);
            if (count >= coupon.getUserLimit()) {
                throw new IllegalArgumentException("优惠券已达单人领取上限");
            }
            //减少优惠券的库存
            redisTemplate.opsForHash().increment(
                    PromotionConstants.COUPON_CACHE_KEY_PREFIX + coupon.getId(),
                    "totalNum", -1);
            //8.发送mq消息,异步完成兑换优惠券的动作
            UserCouponDTO uc = new UserCouponDTO();
            uc.setUserId(UserContext.getUser());
            uc.setCouponId(exchangeTargetId);
            uc.setSerialNum((int) serialNum);
            rabbitMqHelper.send(MqConstants.Exchange.Promotion_EXCHANGE,
                    MqConstants.Key.COUPON_RECEIVE,
                    uc);
            //7.更新优惠券和兑换码
//            synchronized (UserContext.getUser().toString().intern()) {
//            String key = "lock:coupon:uid:" + UserContext.getUser();
//            RLock lock = redissonClient.getLock(key);
//            try {
//                boolean islock = lock.tryLock();
//                if (!islock) {
//                    throw new BizIllegalException("操作过于频繁");
//                }
//            } finally {
//                lock.unlock();//释放锁 - 原子动作
//            }
//            }
        } catch (Exception e) {
            //10.回滚bitmap
            redisTemplate.opsForValue().setBit(PromotionConstants.COUPON_CODE_MAP_KEY,
                    serialNum, false);
            throw e;
        }
    }

//    @MyLock(key = "lock:coupon:uid:#{userId}",
//            type = MyLockType.RE_ENTRANT_LOCK,
//            waitTime = 5,
//            strategy = MyLockStrategy.FAIL_AFTER_RETRY_TIMEOUT)
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCouponAndCode(Long couponId, Integer serialNum ,Long userId) {
        //查询优惠券和兑换码
        Coupon coupon = couponMapper.selectById(couponId);
        if(coupon == null) {
            throw new BizIllegalException("优惠券不存在！");
        }
        ExchangeCode byId = exchangeCodeService.getById(serialNum);
        //修改优惠券的领取数量
        int i = couponMapper.updateIssueNum(couponId);
        if (i == 0) {
            throw new IllegalArgumentException("优惠券已发放完");
        }
        //修改兑换码的状态
        if (byId != null) {
            exchangeCodeService.lambdaUpdate()
                    .eq(ExchangeCode::getId, byId.getId())
                    .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
                    .set(ExchangeCode::getUserId, userId)
                    .update();
        }
        //3.更新用户券
        saveUserCoupon(userId,coupon);
    }

    //更新手动领取的优惠券的记录以及用户券的记录
    @Override //无需再加锁,在receiveCoupon上已经加了悲观锁
    @Transactional(rollbackFor = Exception.class)
    public void updateCouponAndCodeNew(Long couponId, Long userId) {
        //1.查询优惠券
        Coupon coupon = couponMapper.selectById(couponId);
        if(coupon == null) {
            throw new BizIllegalException("优惠券不存在！");
        }
        //2.更新优惠券的已经发放的数量 + 1
        int i = couponMapper.updateIssueNum(couponId); //加了乐观锁
        if(i == 0){
            throw new BizIllegalException("优惠券已发放完！");
        }
        //3.更新用户券
        saveUserCoupon(userId,coupon);
    }

    @Override
    public PageDTO<UserCouponPageVO> queryPage(UserCouponQuery query) {
        Integer status = query.getStatus();
        //1.去数据库中进行查询
        Page<UserCoupon> page = this.lambdaQuery()
                .eq(UserCoupon::getStatus, status)
                .eq(UserCoupon::getUserId, UserContext.getUser())
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        if(page == null){
            return PageDTO.empty(0L,0L);
        }
        //2.转换成VO
        List<UserCoupon> records = page.getRecords();
        Set<Long> CouponIds = records.stream()
                .map(UserCoupon::getCouponId).collect(Collectors.toSet());
        List<Coupon> coupons = couponMapper.selectBatchIds(CouponIds);
        List<UserCouponPageVO> vos = BeanUtils.copyList(coupons, UserCouponPageVO.class);
        //3.返回结果
        return PageDTO.of(page,vos);
    }

    @Override
    public List<CouponDiscountDTO> queryAvailableCoupons(List<OrderCourseDTO> courses) {
        //1.先查找出该用户可用的所有优惠券
        List<Coupon> MyCoupons = this.getBaseMapper().queryMyCoupons(UserContext.getUser());
        if(CollUtils.isEmpty(MyCoupons)) {
            return CollUtils.emptyList(); //返回一个空集合
        }
        //2.初筛 - 通过课程的总价和优惠券的门槛进行比较,不符合条件的优惠券筛出去
        int sum = courses.stream().mapToInt(OrderCourseDTO::getPrice).sum();
        MyCoupons = MyCoupons.stream().filter(coupon -> DiscountStrategy
                .getDiscount(coupon.getDiscountType())
                .canUse(sum, coupon)).collect(Collectors.toList());
        //3.细筛 - 通过优惠券的限定范围去查看所购买的课程中是否有在范围内的,不满足就筛出去
        //有在范围内的,就统计范围内的课程总价是否到达门槛,不满足也筛出去
        Map<Coupon,List<OrderCourseDTO>> couponToCourses = findAvailableCoupons(courses,MyCoupons);
        if(CollUtils.isEmpty(couponToCourses)) {
            return CollUtils.emptyList();
        }
        //将map转list得到真正可用的优惠券集合
        List<Coupon> coupons = new ArrayList<>(couponToCourses.keySet());
        //4.进行优惠券的排列组合,计算出最优解
        //4.1进行排列组合
        List<List<Coupon>> solutions = PermuteUtil.permute(coupons);
        for (Coupon coupon : couponToCourses.keySet()) {
            solutions.add(List.of(coupon)); //添加单券到组合中
        }
        //4.2计算每个组合的总优惠
        List<CouponDiscountDTO> dtos = Collections.synchronizedList(new ArrayList<>(solutions.size()));
        //4.3多线程计算每种策略的优惠值
        CountDownLatch lath = new CountDownLatch(solutions.size());
        for (List<Coupon> solution : solutions) {
            CompletableFuture.supplyAsync(new Supplier<Object>() {
                @Override
                public Object get() {
                    CouponDiscountDTO dto = calculatePerfectDiscount(solution,couponToCourses,courses);
                    return dto;
                }
            },calculateExec).thenAccept(dto ->{
                        dtos.add((CouponDiscountDTO)dto);
                        lath.countDown();
                    });
        }
        //4.4等待线程计算完毕
        try {
            lath.await(5, TimeUnit.SECONDS); //等到5秒,如果线程还没有计算完毕,就继续执行下面的代码
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

//        for (List<Coupon> solution : solutions) {
//            CouponDiscountDTO dto = calculatePerfectDiscount(solution,couponToCourses,courses);
//            dtos.add(dto);
//        }
        //5.将最优解返回给前端
        if(dtos.isEmpty()){
            return CollUtils.emptyList();
        }
        return findBestResult(dtos);
    }

    @Override
    public CouponDiscountDTO queryCouponDiscount(OrderCouponDTO orderCouponDTO) {
        // 1.查询用户优惠券
        List<Long> userCouponIds = orderCouponDTO.getUserCouponIds();
        List<Coupon> coupons = new ArrayList<>(userCouponIds.size());
        for (Long userCouponId : userCouponIds) {
            //按该策略查询出用户优惠券中可用的优惠券
            Long coupon = this.getById(userCouponId).getCouponId();
            Coupon coupon1 = couponMapper.selectById(coupon);
            coupons.add(coupon1);
        }
        if(CollUtils.isEmpty(coupons)) {
            return null;
        }
        //2.查找出用户优惠券中可用的优惠券
        Map<Coupon,List<OrderCourseDTO>> couponToCourses = 
                findAvailableCoupons(orderCouponDTO.getCourseList(),coupons);
        if(CollUtils.isEmpty(couponToCourses)) {
            return null;
        }
        List<Coupon> collect = new ArrayList<>(couponToCourses.keySet());
        //3.返回优惠明细
        return calculatePerfectDiscount(collect,couponToCourses,orderCouponDTO.getCourseList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void useCoupon(List<Long> UserCouponIds) {
        //1.查询用户券
        List<UserCoupon> userCoupons = listByIds(UserCouponIds);
        if(CollUtils.isEmpty(userCoupons)) {
            return;
        }
        //2.过滤不可使用的优惠券
        List<UserCoupon> list = userCoupons.stream()
                .filter(uc -> {
                    if (uc == null) {
                        return false;
                    }
                    if (uc.getStatus() != UserCouponStatus.UNUSED.getValue()) {
                        return false;
                    }
                    if (uc.getTermEndTime().isBefore(LocalDateTime.now())
                            || uc.getTermBeginTime().isAfter(LocalDateTime.now())) {
                        return false;
                    }
                    return true;
                })
                .collect(Collectors.toList());

        list.forEach(coupon->{
            //3.更新状态
            coupon.setStatus(UserCouponStatus.USED.getValue());
            coupon.setUsedTime(LocalDateTime.now());
        });

        //4.更新到数据库
        boolean success = updateBatchById(list);
        if(!success){
            throw new IllegalArgumentException("优惠券核销出错!");
        }
        //5.更新优惠券的已使用数量
        int num = 0;
        for (UserCoupon coupon : list) {
            int i = couponMapper.updateUsedNum(coupon.getCouponId());
            num += i;
        }
        if(num < list.size()){
            throw new IllegalArgumentException("优惠券核销出错!");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundCoupon(List<Long> userCouponIds) {
        //1.查询客户优惠券
        List<UserCoupon> userCoupons = listByIds(userCouponIds);
        if(CollUtils.isEmpty(userCoupons)) {
            return;
        }
        //2.过滤无需调整的优惠券
        List<UserCoupon> collect = userCoupons.stream()
                .filter(uc -> {
                    if(uc == null) {
                        return false;
                    }
                    if (uc.getStatus() != UserCouponStatus.USED.getValue()) {
                        return false;
                    }
                    return true;
                }).collect(Collectors.toList());
        //3.更新状态
        collect.forEach(coupon->{
            if(coupon.getTermEndTime().isBefore(LocalDateTime.now())){
                coupon.setStatus(UserCouponStatus.EXPIRED.getValue());
            }else{
                coupon.setStatus(UserCouponStatus.UNUSED.getValue());
            }
            coupon.setUsedTime(null);
        });
        //4.更新到数据库
        boolean success = updateBatchById(collect);
        if(!success){
            throw new IllegalArgumentException("优惠券退款出错!");
        }
        //5.更新优惠券的已使用数量
        int num = 0;
        for (UserCoupon coupon : collect) {
            int i = couponMapper.rollbackUsedNum(coupon.getCouponId());
            num += i;
        }
        if(num < collect.size()){
            throw new IllegalArgumentException("优惠券退款出错!");
        }
    }

    @Override
    public List<String> queryDiscountRules(List<Long> userCouponIds) {
        //1.查询出优惠券
        List<Long> couponIds = listByIds(userCouponIds).stream()
                .map(UserCoupon::getCouponId).collect(Collectors.toList());
        if(CollUtils.isEmpty(couponIds)) {
            return null;
        }

        return couponMapper.selectBatchIds(couponIds).stream()
                .map(c ->
                        DiscountStrategy.getDiscount(c.getDiscountType())
                                .getRule(c)).collect(Collectors.toList());
    }

    private List<CouponDiscountDTO> findBestResult(List<CouponDiscountDTO> dtos) {
        //1.初始化两个map
        Map<String,CouponDiscountDTO> moreDiscount = new HashMap<>();
        Map<Integer,CouponDiscountDTO> lessCoupon = new HashMap<>();
        //2.遍历策略结合,挑选出两种方案的分别最优解
        for (CouponDiscountDTO dto : dtos) {
            //2.1取出策略的优惠券id组成一个字符串
            String ids = dto.getIds().stream().sorted(Comparator.comparing(Long::longValue))
                    .map(String::valueOf)//排完序后,将每个优惠券的id转换为字符串
                    .collect(Collectors.joining(",")); //将优惠券的id拼接成一个字符串
            //2.2从moreDiscount取出相同券的优惠值
            CouponDiscountDTO couponDiscountDTO = moreDiscount.get(ids);
            if(couponDiscountDTO != null && couponDiscountDTO.getDiscountAmount() > dto.getDiscountAmount()) {
                continue; //当前优惠券的优惠值小于等于之前的优惠券,则跳过
            }
            //2.3从lessCoupon取出相同优惠值的方案,比较使用的券的数量
            CouponDiscountDTO couponDiscountDTO1 = lessCoupon.get(dto.getDiscountAmount());
            if(couponDiscountDTO1 != null && couponDiscountDTO1.getIds().size() <= dto.getIds().size()){
                continue;
            }
            //2.4否则就说明当前的方案更优
            moreDiscount.put(ids,dto);
            lessCoupon.put(dto.getDiscountAmount(),dto);
        }
        //3.求两个map的交集
        Collection<CouponDiscountDTO> collect = CollUtils.intersection(moreDiscount.values(), lessCoupon.values());
        //4.对最终的结果按金额进行排序
        return collect.stream()
                .sorted(Comparator.comparing(CouponDiscountDTO::getDiscountAmount).reversed())
                .collect(Collectors.toList());
    }

    private CouponDiscountDTO calculatePerfectDiscount(List<Coupon> solution,
                                                       Map<Coupon,List<OrderCourseDTO>> couponToCourses,
                                                       List<OrderCourseDTO> courses) {
        //1.初始化优惠明细map
        Map<Long,Integer> m = courses.stream().collect(Collectors.toMap(
                OrderCourseDTO::getId,
                s->0
        ));
        //2.遍历方案集合,取出其中的优惠券依次计算优惠值
        int AllDisCount = 0;
        List<Long> ids = new ArrayList<>();
        List<String> rules = new ArrayList<>();
        for (Coupon coupon : solution) {
            //2.1取出该优惠券可用的课程集合并遍历课程
            List<OrderCourseDTO> course = couponToCourses.get(coupon);
            Integer totalPrice = 0;
            for (OrderCourseDTO c : course) {
                //2.2取出明细map中该课程的优惠明细
                Integer Discount = m.get(c.getId());
                totalPrice += c.getPrice() - Discount;
            }
            //2.3计算当前的每个课程价格,并与该优惠券的门槛值进行对比判断其是否可用
            if(totalPrice >= coupon.getThresholdAmount()){
                ids.add(coupon.getId()); //将可用优惠券的id加到ids集合中
                //2.4如果可用,则计算其分到每个课程的优惠值
                Discount discount = DiscountStrategy.getDiscount(coupon.getDiscountType());
                int totalDisCount = discount.calculateDiscount(totalPrice, coupon); //总优惠
                rules.add(discount.getRule(coupon)); //将可用优惠券的规则加到rules集合中
                AllDisCount = totalDisCount;
                //2.5更新优惠明细map
                Integer dis = totalDisCount / courses.size(); //单个课程的优惠
                int index = 0;
                for (OrderCourseDTO c : course) {
                    if(++index >= courses.size()){
                        m.put(c.getId(),totalDisCount);
                        break; //最后一门课程的优惠用总优惠减去之前的优惠
                    }
                    m.put(c.getId(),m.get(c.getId())+dis);
                    totalDisCount -= dis;
                }
            }else{
                continue; //当前优惠券不可用,则跳过
            }
        }
        //3.累加每个优惠券的优惠金额,赋值,并返回
        CouponDiscountDTO result = new CouponDiscountDTO();
        result.setIds(ids);
        result.setRules(rules);
        result.setDiscountAmount(AllDisCount);
        result.setDiscountDetail(m);
        return result;
    }

    private Map<Coupon, List<OrderCourseDTO>> findAvailableCoupons(List<OrderCourseDTO> courses,
                                                                   List<Coupon> myCoupons) {
        Map<Coupon, List<OrderCourseDTO>> result = new HashMap<>(myCoupons.size());
        for (Coupon c : myCoupons) {
            //1.查找出优惠券的所有可用课程
            //1.1判断优惠券是否限定了范围
            if(c.getSpecific()){
                //1.2限定了范围,查看课程是否在范围内
                List<Long> BizIds = couponScopeService.lambdaQuery()
                        .eq(CouponScope::getCouponId, c.getId())
                        .list().stream().map(CouponScope::getBizId).collect(Collectors.toList());
                List<OrderCourseDTO> l = new ArrayList<>();
                for (OrderCourseDTO cours : courses) {
                    Long cateId = cours.getCateId();
                    Long id = cours.getId();
                    if(BizIds.contains(id) || BizIds.contains(cateId)){
                        l.add(cours); //1.3将该优惠券可用的课程放入集合中
                    }
                } //1.4将符合条件的优惠券和课程集合放入map中
                result.put(c,l);
            }
            //1.5没限定范围的就把所有课程都加到map中
            result.put(c,courses);
        }
        //2.筛选出优惠券的所有可用课程之后,依次计算每个优惠券对应课程的总价是否达到门槛
        result = result.entrySet().stream()
                .filter(new Predicate<Map.Entry<Coupon, List<OrderCourseDTO>>>() {
                    @Override
                    public boolean test(Map.Entry<Coupon, List<OrderCourseDTO>> e) {
                        List<OrderCourseDTO> value = e.getValue();
                        Coupon key = e.getKey();
                        int totalPrice = value.stream()
                                .mapToInt(OrderCourseDTO::getPrice).sum();
                        if (totalPrice >= key.getThresholdAmount()) {
                            return true;
                        }
                        //2.1没到达门槛的也把优惠券筛出去
                        return false;
                    }
                }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        //3.返回结果
        return result;
    }

    public void saveUserCoupon(Long userId,Coupon coupon) {
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(userId);
        userCoupon.setCouponId(coupon.getId());
        if(coupon.getTermBeginTime() == null && coupon.getTermEndTime() == null) {
            userCoupon.setTermBeginTime(LocalDateTime.now());
            userCoupon.setTermEndTime(LocalDateTime.now().plusDays(coupon.getTermDays()));
        }
        userCoupon.setTermBeginTime(coupon.getTermBeginTime());
        userCoupon.setTermEndTime(coupon.getTermEndTime());
        this.save(userCoupon);
    }
}
